package com.google.code.newpath.core.beanresource;

import java.lang.reflect.Field;

import com.google.code.newpath.core.Context;
import com.google.code.newpath.core.Path;
import com.google.code.newpath.core.Resource;
import com.google.code.newpath.core.ResourceInstanceEvent;
import com.google.code.newpath.core.ResourceInstanceProcessor;
import com.google.code.newpath.core.annotation.ContextIn;
import com.google.code.newpath.core.annotation.Lifecycle;
import com.google.code.newpath.core.annotation.ResourceIn;
import com.google.code.newpath.core.annotation.TargetIn;
import com.google.code.newpath.core.exception.ResourceInstanceException;
import com.google.code.newpath.core.exception.SysRuntimeException;
import com.google.code.newpath.util.ReflectionUtils;

public class AnnotationInstanceEvent implements ResourceInstanceEvent<Object> {
	
	

	public Object instanceTarget(ResourceInstanceProcessor<Object> processor, Context context, Resource<Object> resource) {

		Object ret = processor.process(context);
		
		weaveProperties(context, ret, resource);
		
		return ret;
	}

	
	/**
	 * 
	 * TODO Should add the annotation supporting in hierarchy supper or class 
	 * 
	 * @param context
	 * @param target
	 */
	protected void weaveProperties(Context context, Object target, Resource<Object> resource) {
		try {
			Class<?> targetClazz = target.getClass();
			Field[] fields = targetClazz.getDeclaredFields();
			for (Field field : fields) {
				TargetIn targetWired = field.getAnnotation(TargetIn.class);
				if (targetWired != null) {
					ReflectionUtils.makeAccessible(field);
					
					Path propPath = new Path(targetWired.path());
					if(propPath.isAbsolute() == false) {
						//Convert propPath to absolute path:  Target ResoucePath + propPath = absolute path
						propPath = new Path(resource.getPath().getParent(), targetWired.path());
					}
					
					Object propValue = context.getTarget(propPath, false, false);
					if(propValue != null) {
						field.set(target, propValue);
					}
					else {
						throw new ResourceInstanceException("Failed to TargetWired to field '" + field.getName() + "' path=" + propPath, resource);
					}
				}
				
				ResourceIn resourceWired = field.getAnnotation(ResourceIn.class);
				if (resourceWired != null) {
					ReflectionUtils.makeAccessible(field);
					
					Path propPath = new Path(targetWired.path());
					if(propPath.isAbsolute() == false) {
						//Convert propPath to absolute path:  Target ResoucePath + propPath = absolute path
						propPath = new Path(resource.getPath().getParent(), targetWired.path());
					}
					
					//FIXME if has config of this propPath
	        		//The Creator.createResource(Path fullPath,  ResourceDefinition resourceDefinition) method will be invoked!
	        		//And configuration will be pop up into the resourceDefinition parameter
					Resource<?> propResource = resource.getCreator().createResource(propPath);
					if(propResource != null) {
						field.set(target, propResource);
					}
					else {
						throw new ResourceInstanceException("Failed to ResourceWired to field '" + field.getName() + "' path=" + propPath, resource);
					}
				}
				
				ContextIn contextWired = field.getAnnotation(ContextIn.class);
				if (contextWired != null) {
					
					Context propContext = context;
					boolean propContextSet = false;
					String designScope = contextWired.scope();
					while(propContext != null) {
						if(designScope.equals(propContext.getScope())) {
							ReflectionUtils.makeAccessible(field);
							field.set(target, propContext);
							propContextSet = true;
							break;
						}
						propContext = propContext.getParent();
					}
					
					if(propContextSet == false) {
						throw new ResourceInstanceException("Failed to ContextWired to field '" + field.getName() + "' path=" + resource.getPath(), resource);
					}

				}
			}

		} 
		catch(SysRuntimeException re) {
			throw re;
		}
		catch(Exception e) {
			throw new ResourceInstanceException("Failed to Annotation Weav for field wire!  Path=" + resource.getPath(), e, resource);
		}
	}




	
	
}
